#include <system.h>

unsigned short *textMemPtr;	// text pointer
int attrib = 0x0F;		// background & foreground colors
int csr_x = 0, csr_y = 0;	// cursor coordinates

/* scroll the screen */
void scroll(void)
{
	unsigned blank, temp;
	
	blank = 0x20 | (attrib << 8);

	/* Row 25 is the end, scroll up */
	if(csr_y >= 25)
	{
		/* move current text on screen back in the buffer */
		temp = csr_y - 25 + 1;
		memcpy(textMemPtr, textMemPtr + temp * 80, (25 - temp) * 80 * 2);

		/* set the memory that occupies the last line of text
		*  to our 'blank' character */
		memsetw(textMemPtr + (25 - temp) * 80, blank, 80);
		csr_y = 25 - 1;
	}
}

/* update hardware cursor */
void move_csr(void)
{
	unsigned temp;

	/* equation for finding the index in a linear chunk of mem
	*  Index = [(y * width) + x] */
	temp = csr_y * 80 + csr_x;

	outportb(0x3D4, 14);
	outportb(0x3D5, temp >> 8);
	outportb(0x3D4, 15);
	outportb(0x3D5, temp);
}

void setCursorPosition(short row, short col)
{
	csr_x = col;
	csr_y = row;
	move_csr();
	//unsigned temp = csr_y * 80 + csr_x;
	//textMemPtr[temp] = textMemPtr[temp] ^ 0x08;
}

void clearScreen()
{
	unsigned blank;
	int i;
	
	/* use 'short' to represent a space with color */
	blank = 0x20 | (attrib << 8);

	/* Sets the entire screen to spaces in our current
	*  color */
	for(i = 0; i < 25; i++)
		memsetw(textMemPtr + i * 80, blank, 80);

	/* Update out virtual cursor, and then move the
	*  hardware cursor */
	csr_x = 0;
	csr_y = 0;
	move_csr();
}

/* puts a single character on the screen */
void putch(unsigned char c)
{
	unsigned short *where;
	unsigned att = attrib << 8;

	/* Handle a backspace, by moving the cursor back one space */
	if(c == 0x08)
	{
		if(csr_x != 0) csr_x--;
	}

	/* Handles a tab by incrementing the cursor's x, but only
	*  to a point that will mke it divisible by 8 */
	else if(c == 0x09)
	{
		csr_x = (csr_x + 8) & ~(8 - 1);
	}

	/* Handles a 'Carriage Return' */
	else if(c == '\r')
	{
		csr_x = 0;
	}
	
	/* Handle newlines like DOS and the BIOS;
	*  as if CR was also there, bring the cursor
	*  to the margin and increment the y value */
	else if(c == '\n')
	{
		csr_x = 0;
		csr_y++;
	}
	
	/* Any character greater than and including a space, is
	*  a printable character. */
	else if (c >= ' ')
	{
		where = textMemPtr + (csr_y * 80 + csr_x);
		*where = c | att; /* Character and attributes: Color */
		csr_x++;
	}
	
	/* if the cursor has reached the edge of screen's width
	*  insert a new line there */
	if(csr_x >= 80)
	{
		csr_x = 0;
		csr_y++;
	}

	/* Scroll the screen if needed, and move the cursor */
	scroll();
	move_csr();
}

/* Uses the above routine to output a string */
void puts(const char *text)
{
	int i;
	
	for (i = 0; i < strlen(text); i++)
	{
		putch(text[i]);
	}
}

/* Sets the forecolor and backcolor that we will use */
void setTextColor(unsigned char forecolor, unsigned char backcolor)
{
	/* Top 4 bytes are the backgroun, bottom 4 bytes 
	*  are the foreground color */
	attrib = (backcolor << 4) | (forecolor & 0x0F);
}

/* Sets our text-mode VGA pointer, then clears the screen */
void init_video(void)
{
	textMemPtr = (unsigned short *)0xB8000;
	clearScreen();
}
